/*
 * Copyright (C) 2015 Bilibili
 * Copyright (C) 2015 Zhang Rui <bbcallen@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.jinyx.player;

import android.content.Context;
import android.view.View;

import androidx.annotation.NonNull;

import com.kstech.mp3604.R;

import java.lang.ref.WeakReference;

/**
 * Features
 * Common
 * remove rarely used ffmpeg components to reduce binary size config/module-lite.sh
 * workaround for some buggy online video.
 * Android
 * platform: API 9~23
 * cpu: ARMv7a, ARM64v8a, x86 (ARMv5 is not tested on real devices)
 * api: MediaPlayer-like
 * video-output: NativeWindow, OpenGL ES 2.0
 * audio-output: AudioTrack, OpenSL ES
 * hw-decoder: MediaCodec (API 16+, Android 4.1+)
 * alternative-backend: android.media.MediaPlayer, ExoPlayer
 * iOS
 * platform: iOS 7.0~10.2.x
 * cpu: armv7, arm64, i386, x86_64, (armv7s is obselete)
 * api: MediaPlayer.framework-like
 * video-output: OpenGL ES 2.0
 * audio-output: AudioQueue, AudioUnit
 * hw-decoder: VideoToolbox (iOS 8+)
 * alternative-backend: AVFoundation.Framework.AVPlayer, MediaPlayer.Framework.MPMoviePlayerControlelr (obselete since iOS 8)
 * NOT-ON-PLAN
 * obsolete platforms (Android: API-8 and below; iOS: pre-6.0)
 * obsolete cpu: ARMv5, ARMv6, MIPS (I don't even have these types of devices…)
 * native subtitle render
 * avfilter supportNOT-ON-PLAN
 * obsolete platforms (Android: API-8 and below; iOS: pre-6.0)
 * obsolete cpu: ARMv5, ARMv6, MIPS (I don't even have these types of devices…)
 * native subtitle render
 * avfilter support
 *
 * Before Build
 * # install homebrew, git, yasm
 * ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
 * brew install git
 * brew install yasm
 *
 * # add these lines to your ~/.bash_profile or ~/.profile
 * # export ANDROID_SDK=<your sdk path>
 * # export ANDROID_NDK=<your ndk path>
 *
 * # on Cygwin (unmaintained)
 * # install git, make, yasm
 * If you prefer more codec/format
 * cd config
 * rm module.sh
 * ln -s module-default.sh module.sh
 * cd android/contrib
 * # cd ios
 * sh compile-ffmpeg.sh clean
 * If you prefer less codec/format for smaller binary size (include hevc function)
 * cd config
 * rm module.sh
 * ln -s module-lite-hevc.sh module.sh
 * cd android/contrib
 * # cd ios
 * sh compile-ffmpeg.sh clean
 * If you prefer less codec/format for smaller binary size (by default)
 * cd config
 * rm module.sh
 * ln -s module-lite.sh module.sh
 * cd android/contrib
 * # cd ios
 * sh compile-ffmpeg.sh clean
 * For Ubuntu/Debian users.
 * # choose [No] to use bash
 * sudo dpkg-reconfigure dash
 * If you'd like to share your config, pull request is welcome.
 * Build Android
 * git clone https://github.com/Bilibili/ijkplayer.git ijkplayer-android
 * cd ijkplayer-android
 * git checkout -B latest k0.8.8
 *
 * ./init-android.sh
 *
 * cd android/contrib
 * ./compile-ffmpeg.sh clean
 * ./compile-ffmpeg.sh all
 *
 * cd ..
 * ./compile-ijk.sh all
 *
 * # Android Studio:
 * #     Open an existing Android Studio project
 * #     Select android/ijkplayer/ and import
 * #
 * #     define ext block in your root build.gradle
 * #     ext {
 * #       compileSdkVersion = 23       // depending on your sdk version
 * #       buildToolsVersion = "23.0.0" // depending on your build tools version
 * #
 * #       targetSdkVersion = 23        // depending on your sdk version
 * #     }
 * #
 * # If you want to enable debugging ijkplayer(native modules) on Android Studio 2.2+: (experimental)
 * #     sh android/patch-debugging-with-lldb.sh armv7a
 * #     Install Android Studio 2.2(+)
 * #     Preference -> Android SDK -> SDK Tools
 * #     Select (LLDB, NDK, Android SDK Build-tools,Cmake) and install
 * #     Open an existing Android Studio project
 * #     Select android/ijkplayer
 * #     Sync Project with Gradle Files
 * #     Run -> Edit Configurations -> Debugger -> Symbol Directories
 * #     Add "ijkplayer-armv7a/.externalNativeBuild/ndkBuild/release/obj/local/armeabi-v7a" to Symbol Directories
 * #     Run -> Debug 'ijkplayer-example'
 * #     if you want to reverse patches:
 * #     sh patch-debugging-with-lldb.sh reverse armv7a
 * #
 * # Eclipse: (obselete)
 * #     File -> New -> Project -> Android Project from Existing Code
 * #     Select android/ and import all project
 * #     Import appcompat-v7
 * #     Import preference-v7
 * #
 * # Gradle
 * #     cd ijkplayer
 * #     gradle
 * Build iOS
 * git clone https://github.com/Bilibili/ijkplayer.git ijkplayer-ios
 * cd ijkplayer-ios
 * git checkout -B latest k0.8.8
 *
 * ./init-ios.sh
 *
 * cd ios
 * ./compile-ffmpeg.sh clean
 * ./compile-ffmpeg.sh all
 *
 * # Demo
 * #     open ios/IJKMediaDemo/IJKMediaDemo.xcodeproj with Xcode
 * #
 * # Import into Your own Application
 * #     Select your project in Xcode.
 * #     File -> Add Files to ... -> Select ios/IJKMediaPlayer/IJKMediaPlayer.xcodeproj
 * #     Select your Application's target.
 * #     Build Phases -> Target Dependencies -> Select IJKMediaFramework
 * #     Build Phases -> Link Binary with Libraries -> Add:
 * #         IJKMediaFramework.framework
 * #
 * #         AudioToolbox.framework
 * #         AVFoundation.framework
 * #         CoreGraphics.framework
 * #         CoreMedia.framework
 * #         CoreVideo.framework
 * #         libbz2.tbd
 * #         libz.tbd
 * #         MediaPlayer.framework
 * #         MobileCoreServices.framework
 * #         OpenGLES.framework
 * #         QuartzCore.framework
 * #         UIKit.framework
 * #         VideoToolbox.framework
 * #
 * #         ... (Maybe something else, if you get any link error)
 * #
 * Support (支持)
 * Please do not send e-mail to me. Public technical discussion on github is preferred.
 * 请尽量在 github 上公开讨论技术问题，不要以邮件方式私下询问，恕不一一回复。
 * License
 * Copyright (c) 2017 Bilibili
 * Licensed under LGPLv2.1 or later
 * ijkplayer required features are based on or derives from projects below:
 *
 * LGPL
 * FFmpeg
 * libVLC
 * kxmovie
 * soundtouch
 * zlib license
 * SDL
 * BSD-style license
 * libyuv
 * ISC license
 * libyuv/source/x86inc.asm
 * android/ijkplayer-exo is based on or derives from projects below:
 *
 * Apache License 2.0
 * ExoPlayer
 * android/example is based on or derives from projects below:
 *
 * GPL
 * android-ndk-profiler (not included by default)
 * ios/IJKMediaDemo is based on or derives from projects below:
 *
 * Unknown license
 * iOS7-BarcodeScanner
 * ijkplayer's build scripts are based on or derives from projects below:
 *
 * gas-preprocessor
 * VideoLAN
 * yixia/FFmpeg-Android
 * kewlbear/FFmpeg-iOS-build-script
 * Commercial Use
 * ijkplayer is licensed under LGPLv2.1 or later, so itself is free for commercial use under LGPLv2.1 or later
 *
 * But ijkplayer is also based on other different projects under various licenses, which I have no idea whether they are compatible to each other or to your product.
 *
 * IANAL, you should always ask your lawyer for these stuffs before use it in your product.
 **/
public final class MeasureHelper {
    private WeakReference<View> mWeakView;

    private int mVideoWidth;
    private int mVideoHeight;
    private int mVideoSarNum;
    private int mVideoSarDen;

    private int mVideoRotationDegree;

    private int mMeasuredWidth;
    private int mMeasuredHeight;

    private int mCurrentAspectRatio = IRenderView.AR_ASPECT_FIT_PARENT;

    public MeasureHelper(View view) {
        mWeakView = new WeakReference<View>(view);
    }

    public View getView() {
        if (mWeakView == null)
            return null;
        return mWeakView.get();
    }

    public void setVideoSize(int videoWidth, int videoHeight) {
        mVideoWidth = videoWidth;
        mVideoHeight = videoHeight;
    }

    public void setVideoSampleAspectRatio(int videoSarNum, int videoSarDen) {
        mVideoSarNum = videoSarNum;
        mVideoSarDen = videoSarDen;
    }

    public void setVideoRotation(int videoRotationDegree) {
        mVideoRotationDegree = videoRotationDegree;
    }

    /**
     * Must be called by View.onMeasure(int, int)
     *
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    public void doMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //Log.i("@@@@", "onMeasure(" + MeasureSpec.toString(widthMeasureSpec) + ", "
        //        + MeasureSpec.toString(heightMeasureSpec) + ")");
        if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270) {
            int tempSpec = widthMeasureSpec;
            widthMeasureSpec  = heightMeasureSpec;
            heightMeasureSpec = tempSpec;
        }

        int width = View.getDefaultSize(mVideoWidth, widthMeasureSpec);
        int height = View.getDefaultSize(mVideoHeight, heightMeasureSpec);
        if (mCurrentAspectRatio == IRenderView.AR_MATCH_PARENT) {
            width = widthMeasureSpec;
            height = heightMeasureSpec;
        } else if (mVideoWidth > 0 && mVideoHeight > 0) {
            int widthSpecMode = View.MeasureSpec.getMode(widthMeasureSpec);
            int widthSpecSize = View.MeasureSpec.getSize(widthMeasureSpec);
            int heightSpecMode = View.MeasureSpec.getMode(heightMeasureSpec);
            int heightSpecSize = View.MeasureSpec.getSize(heightMeasureSpec);

            if (widthSpecMode == View.MeasureSpec.AT_MOST && heightSpecMode == View.MeasureSpec.AT_MOST) {
                float specAspectRatio = (float) widthSpecSize / (float) heightSpecSize;
                float displayAspectRatio;
                switch (mCurrentAspectRatio) {
                    case IRenderView.AR_16_9_FIT_PARENT:
                        displayAspectRatio = 16.0f / 9.0f;
                        if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270)
                            displayAspectRatio = 1.0f / displayAspectRatio;
                        break;
                    case IRenderView.AR_4_3_FIT_PARENT:
                        displayAspectRatio = 4.0f / 3.0f;
                        if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270)
                            displayAspectRatio = 1.0f / displayAspectRatio;
                        break;
                    case IRenderView.AR_ASPECT_FIT_PARENT:
                    case IRenderView.AR_ASPECT_FILL_PARENT:
                    case IRenderView.AR_ASPECT_WRAP_CONTENT:
                    default:
                        displayAspectRatio = (float) mVideoWidth / (float) mVideoHeight;
                        if (mVideoSarNum > 0 && mVideoSarDen > 0)
                            displayAspectRatio = displayAspectRatio * mVideoSarNum / mVideoSarDen;
                        break;
                }
                boolean shouldBeWider = displayAspectRatio > specAspectRatio;

                switch (mCurrentAspectRatio) {
                    case IRenderView.AR_ASPECT_FIT_PARENT:
                    case IRenderView.AR_16_9_FIT_PARENT:
                    case IRenderView.AR_4_3_FIT_PARENT:
                        if (shouldBeWider) {
                            // too wide, fix width
                            width = widthSpecSize;
                            height = (int) (width / displayAspectRatio);
                        } else {
                            // too high, fix height
                            height = heightSpecSize;
                            width = (int) (height * displayAspectRatio);
                        }
                        break;
                    case IRenderView.AR_ASPECT_FILL_PARENT:
                        if (shouldBeWider) {
                            // not high enough, fix height
                            height = heightSpecSize;
                            width = (int) (height * displayAspectRatio);
                        } else {
                            // not wide enough, fix width
                            width = widthSpecSize;
                            height = (int) (width / displayAspectRatio);
                        }
                        break;
                    case IRenderView.AR_ASPECT_WRAP_CONTENT:
                    default:
                        if (shouldBeWider) {
                            // too wide, fix width
                            width = Math.min(mVideoWidth, widthSpecSize);
                            height = (int) (width / displayAspectRatio);
                        } else {
                            // too high, fix height
                            height = Math.min(mVideoHeight, heightSpecSize);
                            width = (int) (height * displayAspectRatio);
                        }
                        break;
                }
            } else if (widthSpecMode == View.MeasureSpec.EXACTLY && heightSpecMode == View.MeasureSpec.EXACTLY) {
                // the size is fixed
                width = widthSpecSize;
                height = heightSpecSize;

                // for compatibility, we adjust size based on aspect ratio
                if (mVideoWidth * height < width * mVideoHeight) {
                    //Log.i("@@@", "image too wide, correcting");
                    width = height * mVideoWidth / mVideoHeight;
                } else if (mVideoWidth * height > width * mVideoHeight) {
                    //Log.i("@@@", "image too tall, correcting");
                    height = width * mVideoHeight / mVideoWidth;
                }
            } else if (widthSpecMode == View.MeasureSpec.EXACTLY) {
                // only the width is fixed, adjust the height to match aspect ratio if possible
                width = widthSpecSize;
                height = width * mVideoHeight / mVideoWidth;
                if (heightSpecMode == View.MeasureSpec.AT_MOST && height > heightSpecSize) {
                    // couldn't match aspect ratio within the constraints
                    height = heightSpecSize;
                }
            } else if (heightSpecMode == View.MeasureSpec.EXACTLY) {
                // only the height is fixed, adjust the width to match aspect ratio if possible
                height = heightSpecSize;
                width = height * mVideoWidth / mVideoHeight;
                if (widthSpecMode == View.MeasureSpec.AT_MOST && width > widthSpecSize) {
                    // couldn't match aspect ratio within the constraints
                    width = widthSpecSize;
                }
            } else {
                // neither the width nor the height are fixed, try to use actual video size
                width = mVideoWidth;
                height = mVideoHeight;
                if (heightSpecMode == View.MeasureSpec.AT_MOST && height > heightSpecSize) {
                    // too tall, decrease both width and height
                    height = heightSpecSize;
                    width = height * mVideoWidth / mVideoHeight;
                }
                if (widthSpecMode == View.MeasureSpec.AT_MOST && width > widthSpecSize) {
                    // too wide, decrease both width and height
                    width = widthSpecSize;
                    height = width * mVideoHeight / mVideoWidth;
                }
            }
        } else {
            // no size yet, just adopt the given spec sizes
        }

        mMeasuredWidth = width;
        mMeasuredHeight = height;
    }

    public int getMeasuredWidth() {
        return mMeasuredWidth;
    }

    public int getMeasuredHeight() {
        return mMeasuredHeight;
    }

    public void setAspectRatio(int aspectRatio) {
        mCurrentAspectRatio = aspectRatio;
    }

    @NonNull
    public static String getAspectRatioText(Context context, int aspectRatio) {
        String text;
        switch (aspectRatio) {
            case IRenderView.AR_ASPECT_FIT_PARENT:
                text = context.getString(R.string.VideoView_ar_aspect_fit_parent);
                break;
            case IRenderView.AR_ASPECT_FILL_PARENT:
                text = context.getString(R.string.VideoView_ar_aspect_fill_parent);
                break;
            case IRenderView.AR_ASPECT_WRAP_CONTENT:
                text = context.getString(R.string.VideoView_ar_aspect_wrap_content);
                break;
            case IRenderView.AR_MATCH_PARENT:
                text = context.getString(R.string.VideoView_ar_match_parent);
                break;
            case IRenderView.AR_16_9_FIT_PARENT:
                text = context.getString(R.string.VideoView_ar_16_9_fit_parent);
                break;
            case IRenderView.AR_4_3_FIT_PARENT:
                text = context.getString(R.string.VideoView_ar_4_3_fit_parent);
                break;
            default:
                text = context.getString(R.string.N_A);
                break;
        }
        return text;
    }
}
